#include "userprog/syscall.h"
#include <stdio.h>
#include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h"

static void syscall_handler (struct intr_frame *);

void
syscall_init (void) 
{
  intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
}

static void
syscall_handler (struct intr_frame *f UNUSED) 
{
  switch (syscall_number) 
    {
      case SYS_HALT:
        {
          sys_halt();
          NOT_REACHED();
          break;
        }
		case SYS_EXIT:
        {
          int status = *((int *) f->esp + 1);
          sys_exit (status);
          NOT_REACHED ();
        }
        break;

      case SYS_EXEC:
        {
          const char *cmdline = *((const char **) f->esp + 1);
          f->eax = sys_exec (cmdline);
        }
        break;
     default:
        printf("[ERROR] system call %d is unimplemented!\n", syscall_number);
        sys_exit(-1);
        break;
    }
  thread_exit ();
}
void
sys_halt (void) 
{
  shutdown_power_off ();
}

void
sys_exit (int status) 
{
  struct thread *cur = thread_current ();
  if (cur->pcb != NULL)
    {
      printf ("%s: exit(%d)\n", cur->pcb->cmdline, status);
      cur->pcb->exitcode = status;
      cur->pcb->exited = true;
    }
  sema_up (&cur->pcb->sema_wait);
  thread_exit ();
}

pid_t
sys_exec (const char *cmdline) 
{
  pid_t pid = process_execute (cmdline);
  return pid;
}

static int
get_user (const uint8_t *uaddr)
{
  int result;
  asm volatile (
    "movl $1f, %0; movzbl %1, %0; 1:"              // if fault, jump to label 1 and set result = -1
    : "=&a" (result) : "m" (*uaddr));
  return result;
}

static bool
put_user (uint8_t *udst, uint8_t byte)
{
  int error_code;
  asm volatile (
    "movl $1f, %0; movb %b2, %1; 1:"              // if fault, jump to label 1 and set error_code = -1
    : "=&a" (error_code), "=m" (*udst) : "q" (byte));
  return error_code != -1;
}

static void
safe_read_int (int *dst, const int *uptr)
{
  for (size_t i = 0; i < sizeof *dst; i++)
    {
      int byte = get_user ((uint8_t *) uptr + i);
      if (byte < 0)
        sys_exit (-1);
      ((uint8_t *) dst)[i] = byte;
    }
}